/*
*	Copyright (c) 2009 Brent Mombourquette
*
*	Permission is hereby granted, free of charge, to any person
*	obtaining a copy of this software and associated documentation
*	files (the "Software"), to deal in the Software without
*	restriction, including without limitation the rights to use,
*	copy, modify, merge, publish, distribute, sublicense, and/or sell
*	copies of the Software, and to permit persons to whom the
*	Software is furnished to do so, subject to the following
*	conditions:
*
*	The above copyright notice and this permission notice shall be
*	included in all copies or substantial portions of the Software.
*
*	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*	OTHER DEALINGS IN THE SOFTWARE.
*
*	This file contains helper methods for the logging process.
*/
// Import a referrence to the BreadCrumbs database
Components.utils.import("resource://BreadCrumbs/database.jsm");

const FOLLOW = 1;	
const OTHER = 0;		// Flags for what style of edge a given entry may have

var turnedOn = false;	// Signifies that the logging process has just been turned on; used to create dotted edge type upon resuming logging

var logHelper = {
	/*
	 * Create a thumbnail image of the window win.
	 * @param {DOM Window} win
	 * @return {String} img			The thumbnail in a base64 string representation
	 */
	extractThumbnail: function(win) {
		var scale = win.innerHeight / win.innerWidth;
		
	    var canvas = document.getElementById("thumbCanvas");
		// Thumbnail is set to 20% of the size of the actual webpage.
		var canvasW = Math.floor(window.innerWidth * 0.2);
		var canvasH = (canvasW * scale) - 5;
		
		canvas.style.width = canvasW+"px";
		canvas.style.height = canvasH+"px";
		canvas.width = canvasW;
		canvas.height = canvasH;

		var img = '';
		try {
		    var ctx = canvas.getContext("2d");
			ctx.clearRect(0, 0, canvasW, canvasH);
			ctx.save();
			ctx.scale(canvasW/win.innerWidth, canvasH/win.innerHeight);
			ctx.drawWindow(win, 0, 0, win.innerWidth, win.innerHeight, "rgb(255,255,255)");
			img = canvas.toDataURL(); 
			ctx.restore();
	    } catch(e) { dump('Thumbnail Error: ' + e.message + '\n'); }
		
		if (img.length < 1000) { // Under 1000 characters is a good indication that the thumbnail did not register properly
			return 'chrome://BreadCrumbs/content/no_thumbnail.png';
		} else {
			return img;
		}
	},
	
	/*
	 * Return the type of link that caused document doc to be reached. The link type is always FOLLOW if
	 * the document has a referrer, unless the logging feature was just turned on - then there might have
	 * been many websites viewed before it was turned back on, so the link type is OTHER. These are
	 * used to distinguish between solid and dotted edges in the graph.
	 * Globals: sorURL [from log.js], turnedOn
	 * @param {DOM} doc
	 * @param {Integer} linkType			See const FOLLOW and const OTHER 
	  */
	extractLinkType: function(doc) {
		var linkType;
		if (doc.referrer && !turnedOn) {
			// Using the document's referrer as the sorURL is more accurate and solves
			// the messy issue of the user moving between multiple tabs
			sorURL = doc.referrer;
			// The following check is for if the user turned logging off at some point and
			// thus doc.referrer isn't logged - so we have no usable information about
			// the source and thus must set this as a root (ie: sorURL is 'EMPTY_SOURCE_ENTRY').
			if (!logHelper.isURLLogged(sorURL)) 
				sorURL = 'EMPTY_SOURCE_ENTRY';
			linkType = FOLLOW;
		} else if (turnedOn) {
			/*
			 * If the logging was just turned on then the link from source to destination may not be direct
			* and thus this ensures that on the resulting graph the edge will be dotted, to
			* so that there might not be a direct relation.
			 */
			linkType = OTHER;
			turnedOn = false;
		} else { // Not a clicked link: could be clicking Home, entering URL manually, clicking a bookmark, etc
			linkType = OTHER;
		}
		return linkType;
	},

	/*
	 * Make sure that there are no other entries of any permutation of url and sorURL already in the database. 
	 * This prevents useless redundencies such as having both url -> sorURL and sorURL -> url, which are
	 * visually indistinguishable in the graph.
	 * @param {String} dURL	The URL currently being prepared for entry into 
	 *					the database (destURL)
	 * @param {String} sURL	The previously entered URL (sorURL)
	 * @return {Boolean}
	 */
	isUnique: function(dURL, sURL) {
		var uniqueString = "SELECT row FROM edgeLog WHERE (destination=?1 AND source=?2) OR (destination=?3 AND source=?4);";
		var uniqueStatement = breadcrumbsDBConn.createStatement(uniqueString);
		uniqueStatement.bindUTF8StringParameter(0, dURL);
		uniqueStatement.bindUTF8StringParameter(1, sURL);
		uniqueStatement.bindUTF8StringParameter(2, sURL);
		uniqueStatement.bindUTF8StringParameter(3, dURL);
	
		var didProcess = false;
		try {
			while(uniqueStatement.executeStep()){
				didProcess = true;
			}
		} finally {
			uniqueStatement.reset();
			uniqueStatement.finalize();
		}
		
		if (!didProcess) {
			// If the while loop for uniqueStatement.executeStep() did not execute then there is no
			// entry in the database that matches our query critera and hence it is unique
			return true;
		} else {
			return false;
		}
	},
	
	/*
	 * Attempt to extract the favicon URL of the current website by finding a direct referrence to it.
	 * @param {Document} doc		The document reference of the website to be logged
	 * @return {String}
	 */
	extractExplicitFavURL: function(doc) {
		var favURL = "";
        var head = doc.getElementsByTagName('head');
		head = head.item(0);
		if (!head) // Sometimes the head tag is empty
			return logHelper.extractPossibleFavURL(doc);
	    var links = head.getElementsByTagName('link');
		// Search all of the <link> tags in the <head> to find one with a rel matching the most common/standard favicon referrences
	    for (var i = 0; i < links.length; i++) {
	        var rel = links.item(i).getAttribute('rel');
	        if (rel.toLowerCase() == 'shortcut icon' || rel.toLowerCase() == 'icon') {
	            var iconHref = links.item(i).getAttribute('href');
				favURL = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newURI(iconHref, null, content.document.baseURIObject).spec;
	        }
	    }
		
		if (favURL != ""){
			return favURL;
		} else {
			return logHelper.extractPossibleFavURL(doc);
		}
    },
	
	/*
	 * Attempt to extract the favicon URL by querying the Firefox database which stores favicons.
	 * @param {Document} doc		The document reference of the website to be logged
	 * @return {String}
	 */
	extractPossibleFavURL: function(doc) {
		var start = doc.baseURIObject.prePath; 
		var end = "/favicon.ico";
		var possibleFav = '';
		// Most favicon URLs are in the form [website domain]%[/favicon.ico]
		var possibleFavRegEx = start + "%" + end;
		// moz_favicons is the Firefox SQLite table which stores favicons
		var getFaviconString = "SELECT moz_favicons.url FROM moz_favicons WHERE url LIKE ?1;";
		var getFaviconStatement = breadcrumbsDBConn.createStatement(getFaviconString);
		getFaviconStatement.bindUTF8StringParameter(0, possibleFavRegEx);
		
		try {
			while(getFaviconStatement.executeStep()){
				if (getFaviconStatement.numEntires != 0)
					possibleFav = getFaviconStatement.getUTF8String(0)
			}
		} finally {
			getFaviconStatement.reset();
			getFaviconStatement.finalize();
		}
		
		if (possibleFav){
			return possibleFav;
		} else {
			// Sometimes the extraction happens too soon, before it is intered into the moz_favicons
			// table, thus this is a good backup
			return start+end;
		}
	},
	
	/*
	 * Check if url is already in the database as a destination entry.
	 * @param {String} url		(This is always sorURL from log.js
	 * @return {Boolean}
	 */
	isURLLogged: function(url) {
		var res = false;
		var checkString = "SELECT destination FROM edgeLog WHERE destination=?1;";
		var checkStatement = breadcrumbsDBConn.createStatement(checkString);
		checkStatement.bindUTF8StringParameter(0,url);
		
		try {
			while (checkStatement.executeStep()) {
				if (checkStatement.numEntires != 0)
					res = true;
			}
		} finally {
			checkStatement.reset();
			checkStatement.finalize();
		}
		return res;
	}
}